`timescale 1ns / 1ps
module NANDFlash_Control(
    input          clk,
    input          dclk_h,
    input          rst_n,

    input          ECCclk,
    input          decode_en,
    input          code_en,
    output         code_success,  //eccs
    output         decode_success,  //eccs
    input[3:0]     block_address_num,
    input[3:0]     page_address_num,

    input[15:0]     page_size,
    input[15:0]     ecc_size,

    input[31:0]    w_data,
    input          w_valid,
    output         w_en,     //we
    output[31:0]   r_data,
    output         r_valid,  //re
    output         r_can_read,
    input          r_en,
    input          wr_clk,

    input[2:0]     cmd,
    input[3:0]     cmd_counter,
    output         cmd_finish, //cs

    input[39:0]    address,

/************************************************************/
    output      F_WPn,
    output      F_WE,   
    output      F_RE,   
    output      F_CLE,   
    output      F_ALE,  

    input[7:0]  F_DQ_i,
    output[7:0] F_DQ_o, 
    output      F_DQ_oen, 

    input       F_DQS_i,
    output      F_DQS_o,
    output      F_DQS_oen,
    input       F_RB
);
reg        wp_r;
assign     F_WPn = wp_r;
reg         write_en;
reg         read_en;
reg         cmd_finish_r;
reg         code_success_r;
reg         decode_success_r;

assign code_success = code_success_r;
assign decode_success = decode_success_r;
assign cmd_finish = cmd_finish_r;

reg[31:0]   r_data_r;
reg         w_en_r;
reg         r_valid_r;
wire        we_s_w;
wire        re_s_w;
wire        cle_w;
wire        ale_w;
wire[7:0]   dq_i_w;
wire[7:0]   dq_o_w;
wire        dq_oen_w;
wire        dqs_i_w;
wire        dqs_o_w;
wire        dqs_oen_w;
wire        rb_s_w;
assign r_data = r_data_r;
assign r_valid = r_valid_r;
assign w_en = w_en_r;
assign F_WE = we_s_w;
assign F_RE = re_s_w;
assign F_CLE = cle_w;
assign F_ALE = ale_w;
assign dq_i_w = F_DQ_i;
assign F_DQ_o = dq_o_w;
assign F_DQ_oen = dq_oen_w;
assign dqs_i_w = F_DQS_i;
assign F_DQS_o = dqs_o_w;
assign F_DQS_oen = dqs_oen_w;
assign rb_s_w = F_RB;

/********************clk control*******************/

/****************************************************************/
reg[3:0] portMode;

parameter CMD_RESET =                 8'hFF;
parameter CMD_READ_ID =               8'h90;
parameter CMD_READ_STATUS =           8'h70;
parameter CMD_READ_PAGE_C0 =          8'h00;
parameter CMD_READ_PAGE_C1 =          8'h30;
parameter CMD_READ_PAGE_CACHE =       8'h31;
parameter CMD_READ_PAGE_CACHE_CASE =  8'h3f;
parameter CMD_READ_MUL_C0 =           8'h00;
parameter CMD_READ_MUL_C1 =           8'h32;
parameter CMD_PROG_PAGE_C0 =          8'h80;
parameter CMD_PROG_PAGE_C1 =          8'h10;
parameter CMD_PROG_PAGE_CACHE_C0 =    8'h80;
parameter CMD_PROG_PAGE_CACHE_C1 =    8'h15;
parameter CMD_PROG_PAGE_MUL_C0 =      8'h80;
parameter CMD_PROG_PAGE_MUL_C1 =      8'h11;
parameter CMD_ERASE_BLOCK_CO =        8'h60;
parameter CMD_ERASE_BLOCK_C1 =        8'hD0;
parameter CMD_ERASE_BLOCK_MUL_C0 =    8'h60;
parameter CMD_ERASE_BLOCK_MUL_C1 =    8'hD1;
parameter CMD_GET_FEATURES =          8'hee;
parameter CMD_SET_FEATURES =          8'hef;


parameter State_command =   3'b000;
parameter State_adderss =   3'b001;
parameter State_WriteData = 3'b010;
parameter State_ReadData  = 3'b011;
parameter State_Idle =      3'b100;
parameter State_WriteWait = 3'b111;

wire[15:0] PAGE_SIZE;//data size + spare size
wire[15:0] SPARE_SIZE;

assign PAGE_SIZE = page_size;
assign SPARE_SIZE = ecc_size; 

reg[2:0]   port_cmd_reg;
reg[7:0]   port_data_in_reg;
wire       port_data_in_en;
wire[7:0]  port_data_out_reg;
wire       port_data_out_vaild;
wire[31:0] port_data_number;
wire[31:0] port_data_counter;

reg[31:0] read_data_number;
reg[31:0] read_data_counter;

reg[31:0] write_data_number;
reg[31:0] write_data_counter;

reg[39:0] address_reg;
reg[31:0] w_features_reg;
reg[31:0] r_features_reg;

/***********************************************************************/

/*********************** flash mode port **************************/

port_selector u_port_selector(
	.WE_s          (we_s_w      ),
    .RE_s          (re_s_w      ),
    .CLE           (cle_w       ),
    .ALE           (ale_w       ),
    .DQ_i          (dq_i_w      ),
    .DQ_o          (dq_o_w      ),
    .DQ_oen        (dq_oen_w    ),
    .DQS_i         (dqs_i_w     ),
    .DQS_o         (dqs_o_w     ),
    .DQS_oen       (dqs_oen_w   ),

    .clkX2         (clk         ),
    .dclk          (dclk_h      ),
    .rst_n         (rst_n       ),

    .portMode      (portMode),
    .Cmd           (port_cmd_reg          ),
    .InputVal      (port_data_in_reg      ),
    .IV_En         (port_data_in_en       ),
    .ReadVal       (port_data_out_reg     ),
    .RV_En         (port_data_out_vaild   ),
    .RWDataNum     (port_data_number      ),
    .RWDataCounter (port_data_counter     )
);


localparam RESET =        6'b000001;
localparam READ_PAGE =    6'b000010;
localparam WRITE_PAGE =   6'b000100;
localparam READ_ID =      6'b001000;  
localparam ERASE_BLOCK =  6'b010000;
localparam GET_FEATURES = 6'b100000;
localparam SET_FEATRES  = 6'b110000;
localparam IDLE =         6'b000000;

parameter tWB = 8'h10;
parameter tWW = 8'h10;
parameter tADL = 8'h09;

reg[5:0] cur_state;

initial begin
        wp_r = 1'b1;
end

reg      address_cycle;
reg      command_cycle;
reg      read_data_cycle;
reg      write_data_cycle;
reg      delay_cycle;
reg      wait_rnb_cycle;

reg      cur_cycle_finish;
reg      read_page_finish;//读页周期完成
reg      write_page_finish;//写页周期完成
reg      erase_block_finish;
reg      read_id_finish;
reg      get_features_finish;
reg      set_features_finish;
reg      reset_finish;

reg      rst_cmd_finish;

reg[7:0] data_r_a;
reg[7:0] data_r_c;
reg[7:0] data_r_wd;

reg[7:0] cmd_r_a;
reg[7:0] cmd_r_c;
reg[7:0] cmd_r_rd;
reg[7:0] cmd_r_wd;

reg      add_cycle_tri_n;  //地址周期触发信号，下降沿触发
reg      add_cycle_fin_n;  //地址周期完成信号，下降沿有效
reg      cmd_cycle_tri_n;
reg      cmd_cycle_fin_n;
reg      rd_cycle_tri_n;
reg      rd_cycle_fin_n;
reg      wd_cycle_tri_n;
reg      wd_cycle_fin_n;
reg      dly_cycle_tri_n;
reg      dly_cycle_fin_n;
reg      wait_rnb_tri_n;
reg      wait_rnb_fin_n;

reg[7:0] command_reg;

reg rd_address_cycle_finish;
reg eb_address_cycle_finish;
reg wd_command_cycle_finish;
reg wd_address_cycle_finish;
reg rd_decoding_finish;
reg rd_p_finish;
reg rc;
always @(*) begin
    read_page_finish = rd_p_finish && rd_decoding_finish && (~rc);
end

reg[3:0] address_number;
reg[15:0] delay_number;

reg rd_rst_n;
reg wd_rst_n;
reg eb_rst_n;
reg rid_rst_n;
reg rst_rst_n;
reg get_features_rst_n;
reg set_features_rst_n;

reg state_changed_n;

always @(*) begin
    if (address_cycle == 0) begin
        port_data_in_reg = data_r_a;
        port_cmd_reg = cmd_r_a;
    end else if(command_cycle == 0) begin
        port_data_in_reg = data_r_c;
        port_cmd_reg = cmd_r_c;
    end else if(read_data_cycle == 0) begin
        port_cmd_reg = cmd_r_rd;
        port_data_in_reg = 0;
    end else if(write_data_cycle == 0) begin
        port_cmd_reg = cmd_r_wd;
        port_data_in_reg = data_r_wd;
    end else begin
        port_cmd_reg = State_Idle;
        port_data_in_reg = 8'h00;
    end
end

assign port_data_number = cur_state == WRITE_PAGE ? write_data_number : read_data_number;
assign port_data_counter = cur_state == WRITE_PAGE ? write_data_counter : read_data_counter;

always @(*) begin
    case (cur_state)
       READ_PAGE : begin
           wp_r = 0;
           wd_cycle_tri_n = 1;
           set_features_rst_n = 1;
           get_features_rst_n = 1;
           rst_rst_n = 1;
            rid_rst_n = 1;
            wd_rst_n = 1;
            eb_rst_n = 1;
            write_data_number = 0;
            address_reg = address;
            cur_cycle_finish = read_page_finish;
            address_number = block_address_num + page_address_num;
            delay_number = tWB;
            if (code_en) begin
                read_data_number = PAGE_SIZE + SPARE_SIZE;
            end else begin
                read_data_number = PAGE_SIZE;
            end
            if (rd_address_cycle_finish) begin  
                command_reg = CMD_READ_PAGE_C1;
                cmd_cycle_tri_n = add_cycle_fin_n;  
                dly_cycle_tri_n = cmd_cycle_fin_n; 
                wait_rnb_tri_n = dly_cycle_fin_n;  
                rd_cycle_tri_n = wait_rnb_fin_n;   
                add_cycle_tri_n = 1;
                rd_rst_n = 1;
            end else begin
                dly_cycle_tri_n = 1;
                wait_rnb_tri_n = 1;
                rd_cycle_tri_n = 1;
                command_reg = CMD_READ_PAGE_C0;
                rd_rst_n = state_changed_n;
                cmd_cycle_tri_n = state_changed_n;        
                add_cycle_tri_n = cmd_cycle_fin_n; 
            end
       end 
       WRITE_PAGE : begin
           wp_r = 1;
           set_features_rst_n = 1;
           rst_rst_n = 1;
           get_features_rst_n = 1;
           rid_rst_n = 1;
           eb_rst_n = 1;
            rd_rst_n = 1;
            rd_cycle_tri_n = 1;
            read_data_number = 0;
            address_reg = address;
            address_number = block_address_num + page_address_num;
            cur_cycle_finish = write_page_finish;
            if (code_en) begin
                write_data_number = PAGE_SIZE + SPARE_SIZE;
            end else begin
                write_data_number = PAGE_SIZE;
            end
            if (wd_address_cycle_finish & wd_command_cycle_finish == 0) begin
                wd_rst_n = 1;
                wait_rnb_tri_n = 1;
                delay_number = tADL;
                command_reg = CMD_PROG_PAGE_C1;
                add_cycle_tri_n = 1;
                dly_cycle_tri_n = add_cycle_fin_n;
                wd_cycle_tri_n = dly_cycle_fin_n;
                cmd_cycle_tri_n = wd_cycle_fin_n;   
            end else if (wd_command_cycle_finish) begin 
                wd_rst_n = 1;   
                cmd_cycle_tri_n = 1;
                add_cycle_tri_n = 1;
                wd_cycle_tri_n = 1;
                delay_number = tWB; 
                dly_cycle_tri_n = cmd_cycle_fin_n;  
                wait_rnb_tri_n = dly_cycle_fin_n;  
                command_reg = 0; 
            end else begin
                wd_cycle_tri_n = 1;
                wait_rnb_tri_n = 1;
                delay_number = tWW;
                command_reg = CMD_PROG_PAGE_C0;
                wd_rst_n = state_changed_n;
                dly_cycle_tri_n = state_changed_n;
                cmd_cycle_tri_n = dly_cycle_fin_n;         
                add_cycle_tri_n = cmd_cycle_fin_n;  
            end
       end
       READ_ID : begin 
           wp_r = 0;
           delay_number = 0;  
           wd_cycle_tri_n = 1;
           dly_cycle_tri_n = 1;
           set_features_rst_n = 1;
           get_features_rst_n = 1;
           rst_rst_n = 1;        
           eb_rst_n = 1;
           wd_rst_n = 1;
           rd_rst_n = 1;
           wait_rnb_tri_n = 1;
           write_data_number = 0;
           address_reg = 40'h00_00_00_00_20;
           cur_cycle_finish = read_id_finish;
           address_number = 8'h01;
           if (portMode == 4'b0010) begin
                read_data_number = 8'h04;     
           end else begin
                read_data_number = 8'h08;
           end
           command_reg = CMD_READ_ID;
           rid_rst_n = state_changed_n;
           cmd_cycle_tri_n = rid_rst_n;
           add_cycle_tri_n = cmd_cycle_fin_n;
           rd_cycle_tri_n = add_cycle_fin_n;
       end
       RESET : begin
           wp_r = 0;
           wd_cycle_tri_n = 1;
           set_features_rst_n = 1;
           get_features_rst_n = 1;
           rid_rst_n = 1;
           eb_rst_n = 1;
           wd_rst_n = 1;
           rd_rst_n = 1;
           rd_cycle_tri_n = 1;
           add_cycle_tri_n = 1;
           address_number = 0;
           read_data_number = 0;
           write_data_number = 0;
           address_reg = 0;
           cur_cycle_finish = reset_finish;
           command_reg = CMD_RESET; 
           rst_rst_n = state_changed_n;    
           if(rst_cmd_finish)begin
                delay_number = tWB;
                dly_cycle_tri_n = cmd_cycle_fin_n;   
                wait_rnb_tri_n = dly_cycle_fin_n;   
                cmd_cycle_tri_n = 1;
           end else begin
                delay_number = 16'hff;
                dly_cycle_tri_n = rst_rst_n;
                wait_rnb_tri_n = dly_cycle_fin_n;
                cmd_cycle_tri_n = wait_rnb_fin_n;
           end       
       end
       ERASE_BLOCK : begin
           wp_r = 1;
           delay_number = 0;
           wd_cycle_tri_n = 1;
           dly_cycle_tri_n = 1;
           set_features_rst_n = 1;
           get_features_rst_n = 1;
           rst_rst_n = 1;
           rid_rst_n = 1;
           wd_rst_n = 1;
           rd_rst_n = 1;
           wait_rnb_tri_n = 1;
           rd_cycle_tri_n = 1;
           read_data_number = 0;
           write_data_number = 0;
           address_reg = 0;
           address_number = block_address_num;
           cur_cycle_finish = erase_block_finish;
           if (eb_address_cycle_finish) begin
               command_reg = CMD_ERASE_BLOCK_C1;
               cmd_cycle_tri_n = add_cycle_fin_n;
               add_cycle_tri_n = 1;
               eb_rst_n = 1;
           end else begin
               eb_rst_n = state_changed_n;
               cmd_cycle_tri_n = eb_rst_n;
               command_reg = CMD_ERASE_BLOCK_CO;
               add_cycle_tri_n = cmd_cycle_fin_n;
           end
       end
       GET_FEATURES : begin
           wp_r = 0;
           wd_cycle_tri_n = 1;
           set_features_rst_n = 1;
           rst_rst_n = 1;
           rid_rst_n = 1;
           eb_rst_n = 1;
           wd_rst_n = 1;
           rd_rst_n = 1;
           rd_cycle_tri_n = 1;
           write_data_number = 0;
           address_number = 8'h01;
           read_data_number = 8'h04;
           address_reg = {32'h0000_0000, address[7:0]};
           delay_number = tWB;

           cur_cycle_finish = get_features_finish;
           command_reg = CMD_GET_FEATURES;
           get_features_rst_n = state_changed_n;
           cmd_cycle_tri_n = get_features_rst_n;
           add_cycle_tri_n = cmd_cycle_fin_n;
           dly_cycle_tri_n = add_cycle_fin_n;
           wait_rnb_tri_n = dly_cycle_fin_n;
           rd_cycle_tri_n = wait_rnb_fin_n;
       end
       SET_FEATRES : begin
           wp_r = 1;
           get_features_rst_n = 1;
           rst_rst_n = 1;
           rid_rst_n = 1;
           eb_rst_n = 1;
           wd_rst_n = 1;
           rd_rst_n = 1;
           rd_cycle_tri_n = 1;
           read_data_number = 0;
           address_reg = address;
           address_number = 8'h01;
           write_data_number = 8'h04;
           delay_number = 8'h10;
           cur_cycle_finish = set_features_finish;
           command_reg = CMD_SET_FEATURES;
           set_features_rst_n = state_changed_n;
           cmd_cycle_tri_n = set_features_rst_n;
           add_cycle_tri_n = cmd_cycle_fin_n;
           wd_cycle_tri_n = add_cycle_fin_n;
           dly_cycle_tri_n = wd_cycle_fin_n;
           wait_rnb_tri_n = dly_cycle_fin_n;
       end
        default: begin
            wp_r = 0;
            address_reg = 0;
            command_reg = 0;
            delay_number = 0;
            write_data_number = 0;
            read_data_number = 0;
            address_number = 0;
            cmd_cycle_tri_n = 1;
            add_cycle_tri_n = 1;
            wait_rnb_tri_n = 1;
            rd_cycle_tri_n = 1;
            wd_cycle_tri_n = 1;
            dly_cycle_tri_n = 1;
            cur_cycle_finish = 1'b0;
            rd_rst_n = 1;
            wd_rst_n = 1;
            eb_rst_n = 1;
            rid_rst_n = 1;
            rst_rst_n = 1;
            get_features_rst_n = 1;
            set_features_rst_n = 1;
        end
    endcase
end

/***********************coding decoding **************************/

reg  ldpc_code_en;
reg  ldpc_decode_en;
wire ldpc_code_finish;
wire ldpc_decode_finish;
wire ldpc_decode_err;
reg  ldpc_r_en;
reg  ldpc_w_en;
reg    [8:0]  ldpc_r_addr;
reg    [8:0]  ldpc_w_addr;
reg    [31:0] ldpc_w_data;
wire   [31:0] ldpc_r_data;

ldpc_top u_ldpc_top(
	.i_clk           (ECCclk            ),
    .i_rst_n         (rst_n             ),
    .i_code_en       (ldpc_code_en           ),
    .i_decode_en     (ldpc_decode_en         ),
    .o_code_finish   (ldpc_code_finish       ),
    .o_decode_finish (ldpc_decode_finish     ),
    .o_decode_error  (ldpc_decode_err        ),
    .i_w_clk         (wr_clk            ),
    .i_w_en          (ldpc_w_en         ),
    .i_w_addr        (ldpc_w_addr       ),
    .i_w_data        (ldpc_w_data       ),
    .i_r_clk         (wr_clk            ),
    .i_r_en          (ldpc_r_en         ),
    .i_r_addr        (ldpc_r_addr       ),
    .o_r_data        (ldpc_r_data       )
);
/**************************LDPC coding*****************************/

localparam OUTPUT_BUFSIZE = 32'd288;
reg[OUTPUT_BUFSIZE - 1 : 0] output_buffer;
reg[11:0]                  out_buffered_size;
reg[11:0]                  data_writed_num;
reg[11:0]                  data_decoded_num;
wire[11:0]                 decoded_buffer_used;
assign decoded_buffer_used = out_buffered_size - data_decoded_num;

localparam INPUT_BUFSIZE = 32'd288;
reg[INPUT_BUFSIZE - 1 : 0] input_buffer;
reg[11:0]                  in_buffered_size;
reg[11:0]                  data_readed_num;
wire can_read_form_out;
wire can_read_form_ldpc;
wire[15:0] target_size;
wire[11:0] input_buffer_used;
assign input_buffer_used = in_buffered_size - data_readed_num;
assign can_read_form_out = input_buffer_used < 12'd28 && in_buffered_size < PAGE_SIZE;
assign can_read_form_ldpc = input_buffer_used < 12'd28 && in_buffered_size < (PAGE_SIZE + SPARE_SIZE);


reg[8:0] temp_addr;
task data_decode;
    if (decode_en == 1) begin
        ldpc_decode_en <= 1;
        if (decoded_buffer_used >= 4) begin
            ldpc_w_en <= 1;
            ldpc_w_data <= output_buffer >> ((decoded_buffer_used - 4) << 3);
            data_decoded_num <= data_decoded_num + 4;
            ldpc_w_addr <= temp_addr;
            temp_addr <= temp_addr + 1;
        end else begin
            ldpc_w_en <= 0;
            ldpc_w_data <= ldpc_w_data;
            data_decoded_num <= data_decoded_num;
            ldpc_w_addr <= ldpc_w_addr;
            temp_addr <= temp_addr;
        end
    end else begin
        ldpc_decode_en <= 0;
        ldpc_w_en <= 0;
        ldpc_w_data <= 0;
        data_decoded_num <= 0;
        ldpc_w_addr <= 0;
        temp_addr <= 0;
    end
endtask


always @(negedge wr_clk or negedge rst_n) begin
    if (!rst_n) begin
        temp_addr <= 0;
        ldpc_code_en <= 0;
        ldpc_decode_en <= 0;
        ldpc_w_en <= 0;
        ldpc_w_data <= 0;
        ldpc_w_addr <= 0;
        data_decoded_num <= 0;
    end else begin
        if (cur_state == WRITE_PAGE && code_en == 1) begin  //编码输入
            ldpc_code_en <= 1;
            if (temp_addr < (PAGE_SIZE >> 2)) begin
                if (w_valid == 1) begin
                    ldpc_w_en <= 1;
                    ldpc_w_data <= w_data;
                    ldpc_w_addr <= temp_addr;
                    temp_addr <= temp_addr + 1;
                end else begin
                    ldpc_w_en <= 0;
                    ldpc_w_data <= ldpc_w_data;
                    ldpc_w_addr <= ldpc_w_addr;
                    temp_addr <= temp_addr;
                end
            end else begin
                ldpc_w_en <= 0;
                ldpc_w_data <= ldpc_w_data;
                ldpc_w_addr <= ldpc_w_addr;
                temp_addr <= temp_addr;               
            end
        end else if(cur_state == READ_PAGE) begin   //译码输入
            data_decode;
        end else begin
            temp_addr <= 0;
            ldpc_code_en <= 0;
            ldpc_decode_en <= 0;
            ldpc_w_en <= 0;
            ldpc_w_data <= 0;
            ldpc_w_addr <= 0;
            data_decoded_num <= 0;
        end
    end
end


reg[8:0] temp1_addr;
always @(posedge wr_clk or negedge rst_n) begin
    if (!rst_n) begin
        temp1_addr <= 0;
        code_success_r <= 0;
        decode_success_r <= 0;
        ldpc_r_en <= 0;
        ldpc_r_addr <= 0;
    end else begin
        if (cur_state == WRITE_PAGE) begin // 编码输出地址
            if (code_en == 1 && ldpc_code_finish == 1) begin
                code_success_r <= 0;
                if (ldpc_r_addr < ((PAGE_SIZE >> 2) + 1)) begin
                    if (can_read_form_ldpc) begin
                        ldpc_r_en <= 1;
                        ldpc_r_addr <= temp1_addr;
                        temp1_addr <= temp1_addr + 1;
                    end else begin
                        ldpc_r_en <= 0;
                        ldpc_r_addr <= ldpc_r_addr;
                        temp1_addr <= temp1_addr;
                    end
                end else begin
                    code_success_r <= 1;
                    ldpc_r_en <= 0;
                    ldpc_r_addr <= 0;
                    temp1_addr <= 0;
                end
            end else begin
                code_success_r <= 0;
                ldpc_r_en <= 0;
                ldpc_r_addr <= 0;
                temp1_addr <= 0;
            end
        end else if(cur_state == READ_PAGE) begin //译码输出地址
            if (decode_en == 1 && ldpc_decode_finish == 1) begin
                decode_success_r <= 0;
                if (temp1_addr <= (PAGE_SIZE >> 2)) begin
                    if (r_en == 1) begin
                        ldpc_r_en <= 1;
                        ldpc_r_addr <= temp1_addr;
                        temp1_addr <= temp1_addr + 1;
                    end else begin
                        ldpc_r_en <= 0;
                        ldpc_r_addr <= ldpc_r_addr;
                        temp1_addr <= temp1_addr;
                    end
                end else begin
                    decode_success_r <= 1;
                    ldpc_r_en <= 0;
                    ldpc_r_addr <= ldpc_r_addr;
                    temp1_addr <= temp1_addr;
                end
            end else begin
                decode_success_r <= 0;
                ldpc_r_en <= 0;
                ldpc_r_addr <= 0;
                temp1_addr <= 0;
            end
        end else begin
            decode_success_r <= 0;
            code_success_r <= 0;
            ldpc_r_en <= 0;
            ldpc_r_addr <= 0;
            temp1_addr <= 0;
        end
    end
end

/**********************LDPC decoding***************************/

/***********************  data in**************************/

reg read_wait;
always @(negedge wr_clk or negedge rst_n) begin
    if (!rst_n) begin
        read_wait <= 0;
        w_en_r <= 0;
        w_features_reg <= 0;
        input_buffer <= 0;
        in_buffered_size <= 0;
    end else begin
        if (cur_state == SET_FEATRES) begin
            if (w_valid == 1) begin
                w_features_reg <= w_data;
            end else begin
                w_features_reg <= w_features_reg;
            end
            if(w_features_reg == 0)begin
                w_en_r <= 1;
            end else begin
                w_en_r <= 0;
            end
        end else if(cur_state == WRITE_PAGE) begin
            if (can_read_form_out) begin
                w_en_r <= 1;
            end else begin
                w_en_r <= 0;
            end
            if (w_valid == 1) begin
                input_buffer <=  {input_buffer[INPUT_BUFSIZE - 32 : 0], w_data};
                in_buffered_size <= in_buffered_size + 4;
            end else if(ldpc_r_en == 1 && ldpc_code_finish == 1) begin  //编码数据输出
                if (read_wait == 0) begin
                    read_wait <= 1;
                    input_buffer <= input_buffer;
                    in_buffered_size <= in_buffered_size;
                end else begin
                    read_wait <= read_wait;
                    input_buffer <=  {input_buffer[INPUT_BUFSIZE - 32 : 0], ldpc_r_data};
                    in_buffered_size <= in_buffered_size + 4;
                end
            end else begin
                input_buffer <= input_buffer;
                in_buffered_size <= in_buffered_size;
            end
        end else begin
            read_wait <= 0; 
            w_en_r <= 0;
            w_features_reg <= 0;
            input_buffer <= 0;
            in_buffered_size <= 0;
        end
    end    
end

/***********************  data out**************************/

wire[11:0] output_buffer_used;
assign output_buffer_used = out_buffered_size - data_writed_num;

wire can_write;
assign can_write = output_buffer_used >= 4 && r_en == 1;



assign r_can_read = cur_state == READ_PAGE ? (decode_en ? (ldpc_decode_finish || rc) : output_buffer_used >= 4) :
                    cur_state == READ_ID ? output_buffer_used >= 4 :
                    cur_state == GET_FEATURES ? output_buffer_used >= 4 : 0;

task data_output;
    if (decode_en == 1) begin
        if (ldpc_r_en == 1 && ldpc_decode_finish == 1) begin  //译码数据输出
            if (temp1_addr <= ((PAGE_SIZE >> 2) + 1)) begin
                rc <= 1;
                if (temp1_addr > 1) begin
                    r_valid_r <= 1;
                    r_data_r <= ldpc_r_data;
                    rd_decoding_finish <= 0;
                end else begin
                    r_valid_r <= 0;
                    r_data_r <= r_data_r;
                    rd_decoding_finish <= 0;
                end
            end else begin
                rc <= 0;
                r_valid_r <= 0;
                r_data_r <= r_data_r;
                rd_decoding_finish <= 1;
            end
        end else begin
            r_valid_r <= 0;
            r_data_r <= r_data_r;
        end
    end else begin
        rd_decoding_finish <= 1;
        if (can_write) begin
            r_valid_r <= 1;
            r_data_r <= output_buffer >> ((output_buffer_used - 4) << 3);
            data_writed_num <= data_writed_num + 4;
        end else begin
            r_valid_r <= 0;
            r_data_r <= r_data_r;
            data_writed_num <= data_writed_num;
        end
    end
endtask

always @(negedge wr_clk or negedge rst_n) begin
    if (!rst_n) begin
        data_writed_num <= 0;
        rd_decoding_finish <= 0;
        r_valid_r <= 0;
        r_data_r <= 0;
        rc <= 0;
    end else begin
        if (cur_state == GET_FEATURES || cur_state == READ_ID) begin
            if (can_write) begin
                r_valid_r <= 1;
                r_data_r <= output_buffer[31:0];
                data_writed_num <= data_writed_num + 4;
            end else begin
                r_valid_r <= 0;
                r_data_r <= r_data_r;
                data_writed_num <= data_writed_num;
            end
        end else if (cur_state == READ_PAGE) begin
            data_output;
        end else begin
            data_writed_num <= 0;
            rd_decoding_finish <= 0;
            r_valid_r <= 0;
            r_data_r <= 0;
            rc <= 0;
        end
    end
end



/****************************main**********************************/
reg[3:0] last_cmd_counter;
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        cur_state <= IDLE;
        last_cmd_counter <= 0;
        cmd_finish_r <= 1;
    end else begin
        if(cur_state == IDLE && cmd_counter != last_cmd_counter)begin
            case (cmd)
               3'b000 : cur_state <= IDLE;
               3'b001 : cur_state <= READ_PAGE;
               3'b010 : cur_state <= WRITE_PAGE;
               3'b011 : cur_state <= ERASE_BLOCK;
               3'b100 : cur_state <= READ_ID;
               3'b101 : cur_state <= GET_FEATURES;
               3'b110 : cur_state <= SET_FEATRES;
               3'b111 : cur_state <= RESET;
               default: cur_state <= IDLE;
            endcase
            if (cmd == 3'b000) begin
                cmd_finish_r <= 1;
            end else begin
                cmd_finish_r <= 0;
            end
        end else begin
            if (cur_cycle_finish) begin
                cur_state <= IDLE;
                last_cmd_counter <= cmd_counter;
                cmd_finish_r <= 1;
            end else begin
                cur_state <= cur_state;
            end
        end
    end
end

/*********************** read page **************************/

always @(posedge address_cycle or negedge rd_rst_n) begin
    if (!rd_rst_n) begin
        rd_address_cycle_finish <= 1'b0;
    end else begin
        if (cur_state == READ_PAGE) begin
            rd_address_cycle_finish <= 1'b1;
        end else begin
            rd_address_cycle_finish <= 1'b0;
        end
    end
end

wire rd_b1;
assign rd_b1 = cur_state == READ_PAGE && rd_address_cycle_finish == 1;

always @(posedge read_data_cycle or negedge rd_rst_n)begin
    if (!rd_rst_n) begin
        rd_p_finish <= 1'b0;
    end else begin
        if (rd_b1) begin
            rd_p_finish <= 1'b1;
        end else begin
            rd_p_finish <= 1'b0;
        end
    end
end

/*********************write page ***************************/

wire wp_b1;
assign wp_b1 = cur_state == WRITE_PAGE & wd_address_cycle_finish == 1;

always @(posedge command_cycle or negedge wd_rst_n) begin
    if (!wd_rst_n) begin
        wd_command_cycle_finish <= 1'b0;
    end else begin
        if (wp_b1) begin
            wd_command_cycle_finish <= 1'b1;
        end else begin
            wd_command_cycle_finish <= 1'b0;
        end
    end
end

always @(posedge address_cycle or negedge rd_rst_n) begin
    if (!rd_rst_n) begin
        wd_address_cycle_finish <= 1'b0;
    end else begin
        if (cur_state == WRITE_PAGE) begin
            wd_address_cycle_finish <= 1'b1;
        end else begin
            wd_address_cycle_finish <= 1'b0;
        end
    end
end

wire wp_b2;
assign wp_b2 = cur_state == WRITE_PAGE && wd_command_cycle_finish == 1;

always @(posedge wait_rnb_fin_n or negedge wd_rst_n)begin
    if (!wd_rst_n) begin
        write_page_finish <= 1'b0;
    end else begin
        if (wp_b2) begin
            write_page_finish <= 1'b1;
        end else begin
            write_page_finish <= 1'b0;
        end
    end
end

/********************erase block******************************/

always @(posedge address_cycle or negedge eb_rst_n) begin
    if (!eb_rst_n) begin
        eb_address_cycle_finish <= 1'b0;
    end else begin
        if (cur_state == ERASE_BLOCK) begin
            eb_address_cycle_finish <= 1'b1;
        end else begin
            eb_address_cycle_finish <= 1'b0;
        end
    end
end

wire eb_b1;
assign eb_b1 = cur_state == ERASE_BLOCK && eb_address_cycle_finish == 1;
always @(posedge cmd_cycle_fin_n or negedge eb_rst_n)begin
    if (!eb_rst_n) begin
        erase_block_finish <= 1'b0;
    end else begin
        if (eb_b1) begin
            erase_block_finish <= 1'b1;
        end else begin
            erase_block_finish <= 1'b0;
        end
    end
end

/************************read id ******************************/

always @(posedge rd_cycle_fin_n or negedge rid_rst_n)begin
    if (!rid_rst_n) begin
        read_id_finish <= 1'b0;
    end else begin
        if (cur_state == READ_ID) begin
            read_id_finish <= 1'b1;
        end else begin
            read_id_finish <= 1'b0;
        end
    end
end

/**********************get features****************************/

always @(posedge read_data_cycle or negedge get_features_rst_n) begin
    if (!get_features_rst_n) begin
        get_features_finish <= 1'b0;
    end else begin
        if (cur_state == GET_FEATURES) begin
            get_features_finish <= 1'b1;
        end else begin
            get_features_finish <= 1'b0;
        end
    end
end

/**********************set features****************************/

//reg[1:0] feat;
always @(posedge wait_rnb_cycle or negedge set_features_rst_n or negedge rst_n) begin
    if (!rst_n) begin
        portMode <= 4'b0010;
        set_features_finish <= 1'b0;
      //  feat <= 0;
    end else begin
        if (!set_features_rst_n) begin
        //portMode <= 4'b0010;
        set_features_finish <= 1'b0;
        //feat <= 0;
    end else begin
        if (cur_state == SET_FEATRES) begin
           // feat <= w_features_reg[5:4];
            set_features_finish <= 1'b1;
            case (w_features_reg[5:4])
            2'b00 : portMode <= 4'b0010;
            2'b01 : portMode <= 4'b0100;
            2'b10 : portMode <= 4'b1000;
            2'b11 : portMode <= 4'b0001;
                default: portMode <= portMode;
            endcase
        end else begin
            portMode <= portMode;
            set_features_finish <= set_features_finish;
        end
    end
    end
end

/**************************reset******************************/
always @(posedge command_cycle or negedge rst_rst_n) begin
    if (!rst_rst_n) begin
        rst_cmd_finish <= 0;
    end else begin
        if (cur_state == RESET) begin
            rst_cmd_finish <= 1;
        end else begin
            rst_cmd_finish <= 0;
        end
    end
end

wire rst_b2;
assign rst_b2 = cur_state == RESET && rst_cmd_finish == 1;
always @(posedge wait_rnb_cycle or negedge rst_rst_n) begin
    if (!rst_rst_n) begin
        reset_finish <= 1'b0;
    end else begin
        if (rst_b2) begin
            reset_finish <= 1'b1;
        end else begin
            reset_finish <= 1'b0;
        end
    end
end

/**************************triggle gen******************************/

reg[5:0] last_state;
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        state_changed_n <= 1'b0;
        last_state <= 6'b111111;
    end else begin
        if (cur_state == last_state) begin
            state_changed_n <= 1'b1;
        end else begin
            state_changed_n <= 1'b0;
        end
        last_state <= cur_state;
    end
end

/**********************beheaver define****************************/
/*********************** address cycle **************************/
reg[3:0] addCounter;
always @(negedge clk or negedge add_cycle_tri_n or negedge rst_n) begin
    if (!rst_n) begin
        address_cycle <= 1'b1;
        addCounter <= 4'h0;
        data_r_a <= 1;
        cmd_r_a <= State_Idle;
    end else begin
        if (add_cycle_tri_n == 1'b1) begin
            if (address_cycle == 1'b0 && addCounter < address_number) begin
                cmd_r_a <= State_adderss;
                if (port_data_in_en & rb_s_w) begin
                    data_r_a <= (address_reg >> (8 * addCounter));
                    addCounter <= addCounter + 1'b1;
                end else begin
                    data_r_a <= data_r_a;
                    addCounter <= addCounter;                    
                end 
            end else begin
                address_cycle <= 1'b1;
                addCounter <= 4'h0;
                cmd_r_a <= State_Idle;
            end
        end else if(add_cycle_tri_n == 1'b0) begin
            address_cycle <= 1'b0;
            addCounter <= 4'h0;
        end else begin
            address_cycle <= 1'b1;
            addCounter <= 4'h0;
        end
    end
end

always @(posedge clk or posedge address_cycle or negedge rst_n) begin
    if (rst_n & clk == 1'b0 & address_cycle == 1'b1) begin
        add_cycle_fin_n <= 1'b0;
    end else begin
        add_cycle_fin_n <= 1'b1;
    end
end

/*********************** command cycle **************************/
reg cmdState;
always @(negedge clk or negedge cmd_cycle_tri_n or negedge rst_n) begin
    if (!rst_n) begin
        cmd_r_c <= State_Idle;
        command_cycle <= 1'b1;
        cmdState <= 1'b0;
        data_r_c <= 0;
    end else begin
        if (cmd_cycle_tri_n == 1'b1) begin
            if (command_cycle == 1'b0 & cmdState == 1'b0) begin
               cmd_r_c <= State_command; 
               if (port_data_in_en & rb_s_w) begin
                    data_r_c <= command_reg;
                    cmdState = 1'b1;
                end else begin
                    data_r_c <= data_r_c;                     
                end
            end else begin
                command_cycle <= 1'b1;
                cmd_r_c <= State_Idle;
                data_r_c <= data_r_c;
            end
        end else if(cmd_cycle_tri_n == 1'b0) begin
            command_cycle <= 1'b0;
            cmdState <= 1'b0;
        end else begin
            command_cycle <= 1'b1;
            cmdState <= 1'b0;
        end
    end
end

always @(posedge clk or posedge command_cycle or negedge rst_n) begin
    if (!rst_n) begin
        cmd_cycle_fin_n <= 1'b1;
    end else begin
        if (command_cycle == 1'b1) begin
            if (clk == 1'b0) begin
                cmd_cycle_fin_n <= 1'b0;
            end else begin
                cmd_cycle_fin_n <= 1'b1;
            end
        end else begin
            cmd_cycle_fin_n <= 1'b1;
        end
    end
end

/*********************** read data cycle **************************/
task read_data;
    begin
       output_buffer <= {output_buffer[OUTPUT_BUFSIZE - 8:0], port_data_out_reg};
       out_buffered_size <= out_buffered_size + 1;
    end
endtask

always @(negedge clk or negedge rd_cycle_tri_n or negedge rst_n) begin
    if (!rst_n) begin
        read_data_cycle <= 1'b1;
        cmd_r_rd <= State_Idle;
        out_buffered_size <= 0;
        read_data_counter <= 32'h0;
        output_buffer <= 0;
    end else begin
        if (rd_cycle_tri_n == 1'b1) begin
            if (read_data_counter < read_data_number & read_data_cycle == 1'b0) begin
                cmd_r_rd <= State_ReadData;
                if (port_data_out_vaild & rb_s_w) begin
                    read_data;
                    read_data_counter <= read_data_counter + 1'b1;
                end else begin
                    read_data_counter <= read_data_counter;
                end
            end else begin
                read_data_cycle <= 1'b1;
                cmd_r_rd <= State_Idle;
            end
        end else if(rd_cycle_tri_n == 1'b0) begin
            read_data_cycle <= 1'b0;
            cmd_r_rd <= State_Idle;
            read_data_counter <= 32'h0;
            out_buffered_size <= 0;
            output_buffer <= 0;
        end
    end
end

always @(posedge clk or posedge read_data_cycle or negedge rst_n) begin
    if (rst_n & clk == 1'b0 & read_data_cycle == 1'b1) begin
        rd_cycle_fin_n <= 1'b0;
    end else begin
        rd_cycle_fin_n <= 1'b1;
    end
end

/*********************** write data cycle **************************/
wire[7:0] inputData;
reg       wait_ecc;
reg       waite_for_data;
task write_data;
    if (cur_state == SET_FEATRES) begin
        data_r_wd <= (w_features_reg >> (write_data_counter << 3));
    end else begin
        data_r_wd = input_buffer >> ((input_buffer_used - 1) << 3);
        data_readed_num <= data_readed_num + 1;
    end
endtask

always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        waite_for_data <= 0;
    end else begin
        if (cur_state == WRITE_PAGE && input_buffer_used == 0) begin
            waite_for_data <= 1;
        end else begin
            waite_for_data <= 0;
        end
    end
end

always @(negedge clk or negedge wd_cycle_tri_n or negedge rst_n) begin
    if (!rst_n) begin
        data_r_wd <= 8'hff;
        write_data_cycle <= 1'b1;
        cmd_r_wd <= State_WriteData;
        write_data_counter <= 32'h0;
        data_readed_num <= 0;
    end else begin
        if (wd_cycle_tri_n == 1'b1) begin
            if (write_data_cycle == 1'b0 && write_data_counter < write_data_number) begin
                if(waite_for_data) begin
                    cmd_r_wd <= State_WriteWait;
                    data_r_wd <= data_r_wd;
                end else begin
                    cmd_r_wd <= State_WriteData;
                    if (port_data_in_en & rb_s_w) begin
                        write_data;
                        write_data_counter <= write_data_counter + 1'b1;
                    end else begin
                        data_r_wd <= data_r_wd;
                        write_data_counter <= write_data_counter;
                    end
                end
            end else begin
                data_r_wd <= 8'hff;
                write_data_cycle <= 1'b1;
                write_data_counter <= 32'h0;
                cmd_r_wd <= State_Idle;
            end
        end else if (wd_cycle_tri_n == 1'b0) begin
            write_data_cycle <= 1'b0;
            write_data_counter <= 32'h0;
            data_readed_num <= 0;
            cmd_r_wd <= State_WriteData;
        end
    end
end

always @(posedge clk or posedge write_data_cycle or negedge rst_n) begin
    if (rst_n & clk == 1'b0 & write_data_cycle == 1'b1) begin
        wd_cycle_fin_n <= 1'b0;
    end else begin
        wd_cycle_fin_n <= 1'b1;
    end
end

/************************** delay cycle *****************************/
reg[15:0] dly_counter;
wire dc_b0;
assign dc_b0 = delay_cycle == 0 && dly_counter < delay_number;
always @(negedge clk or negedge dly_cycle_tri_n or negedge rst_n) begin
    if (!rst_n) begin
        delay_cycle <= 1'b1;
        dly_counter <= 0;//////////////////
    end else begin
        if (dly_cycle_tri_n == 1) begin
            if (dc_b0) begin
                dly_counter <= dly_counter + 1;
            end else begin
                delay_cycle <= 1'b1;
                dly_counter <= dly_counter;
            end
        end else begin
            delay_cycle <= 1'b0;
            dly_counter <= 0;
        end
    end
end

always @(posedge clk or posedge delay_cycle or negedge rst_n) begin
        if (rst_n & clk == 1'b0 & delay_cycle == 1'b1) begin
        dly_cycle_fin_n <= 1'b0;
    end else begin
        dly_cycle_fin_n <= 1'b1;
    end
end

/************************** wait rnb cycle *****************************/

always @(negedge clk or negedge wait_rnb_tri_n or negedge rst_n) begin
    if (!rst_n) begin
        wait_rnb_cycle <= 1;
    end else begin
        if (wait_rnb_tri_n == 1) begin
            if (wait_rnb_cycle == 0 && rb_s_w == 0) begin
                wait_rnb_cycle <= 0;
            end else begin
                wait_rnb_cycle <= 1;
            end
        end else begin
            wait_rnb_cycle <= 1'b0;
        end
    end
end

always @(posedge clk or posedge wait_rnb_cycle or negedge rst_n) begin
        if (rst_n & clk == 1'b0 & wait_rnb_cycle == 1'b1) begin
        wait_rnb_fin_n <= 1'b0;
    end else begin
        wait_rnb_fin_n <= 1'b1;
    end
end

endmodule // NANDFlash_Control